#include <all_header.h>

typedef struct console_read_dma_param {
	uint32_t start;
	uint32_t size;
} dma_param_t;

static ring_buffer_handle_t handle;
static uint8_t dma_read_buffer[CONSOLE_DMA_READ_BUFFER_SIZE];
static QueueHandle_t que;
static dma_param_t param;

typedef enum {
	INTERRUPT_UART_TIMEOUT, INTERRUPT_DMA_CMPLT, INTERRUPT_DMA_HALF_CMPLT,
} interrupt_type_t;

static void process_interrupt(interrupt_type_t type);

void console_read_task_dma_cmpt_callback(void) {
	process_interrupt(INTERRUPT_DMA_CMPLT);
}

void console_read_task_dma_half_cmpt_callback(void) {
	process_interrupt(INTERRUPT_DMA_HALF_CMPLT);
}

void console_read_task_uart_timeout_callback(void) {
	process_interrupt(INTERRUPT_UART_TIMEOUT);
}

static void channel5_transfer_complete(DMA_HandleTypeDef *dma) {
	ring_buffer_write_cmplt_callback(handle, dma);
}

static void channel6_transfer_complete(DMA_HandleTypeDef *dma) {
	ring_buffer_read_cmplt_callback(handle, dma);
}

static void transfer_failed(DMA_HandleTypeDef *dma) {
	ASSERT(0);
}

void console_read_task_init(void) {
	MEM_ZERO(param);
	que = xQueueCreate(1, sizeof(dma_param_t));
	ASSERT(que);

	handle = ring_buffer_create(CONSOLE_READ_BUFFER_SIZE,
			&handle_GPDMA1_Channel5, &handle_GPDMA1_Channel6);
	ASSERT(handle);
	HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel5, HAL_DMA_XFER_CPLT_CB_ID,
			channel5_transfer_complete);
	HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel6, HAL_DMA_XFER_CPLT_CB_ID,
			channel6_transfer_complete);
	HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel5, HAL_DMA_XFER_ERROR_CB_ID,
			transfer_failed);
	HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel6, HAL_DMA_XFER_ERROR_CB_ID,
			transfer_failed);

	HAL_UART_ReceiverTimeout_Config(&huart1,
			huart1.Init.BaudRate * CONSOLE_RECV_TIMEOUT / 1000);

	ASSERT(HAL_UART_EnableReceiverTimeout(&huart1) == HAL_OK);

	ASSERT(
			HAL_UART_Receive_DMA(&huart1, dma_read_buffer,
					sizeof(dma_read_buffer)) == HAL_OK);
}

static void process_interrupt(interrupt_type_t type) {
	uint32_t const size = handle_GPDMA1_Channel1.Instance->CDAR
			- (uint32_t) dma_read_buffer;
	if (param.start == size) {
		return;
	}

	param.size = (size == 0 ? sizeof(dma_read_buffer) : size) - param.start;
	BaseType_t val = pdFALSE;
	ASSERT(xQueueSendFromISR(que,&param,&val) == pdTRUE);

	param.start = size;
}

void console_read_task_run(void *arg) {
	dma_param_t dma;
	while (true) {
		ASSERT(xQueueReceive(que,&dma,portMAX_DELAY) == pdTRUE);
		ASSERT(dma.size <= ring_buffer_get_available_size(handle));
		ASSERT(
				ring_buffer_write(handle, &dma_read_buffer[dma.start], dma.size) == dma.size);
	}
}

uint32_t console_read_task_read(uint8_t *data, uint32_t size,
		uint32_t wait_ticks) {
	return ring_buffer_read(handle, data, size, wait_ticks);
}

void console_read_task_clear(void) {
	ring_buffer_clear(handle);
}
